1 /** 2 * Managed memory is a safe wrapper around existing heap allocated memory. 3 * Safe heap allocated memory ensures that it cannot be deallocated until such time 4 * it is no longer used. 5 * 6 * This has similar usages with refcounting and duplication. 7 * 8 * License: 9 * Copyright Devisualization (Richard Andrew Cattermole) 2014 - 2017. 10 * Distributed under the Boost Software License, Version 1.0. 11 * (See accompanying file LICENSE_1_0.txt or copy at 12 * http://www.boost.org/LICENSE_1_0.txt) 13 */ 14 module devisualization.util.core.memory.managed; 15 import stdx.allocator : ISharedAllocator, IAllocator, make, dispose, processAllocator, theAllocator; 16 import std.typecons : Tuple; 17 import std.traits : CopyConstness, isBasicType, isPointer, ForeachType, isArray, isDynamicArray, Unqual; 18 19 version(Windows) { 20 import core.sys.windows.com : IUnknown; 21 } 22 23 /** 24 * Defaults to using ReferenceCountedManager as memory manager 25 * 26 * See_Also: 27 * ReferenceCountedManager 28 */ 29 auto managers() { 30 MemoryManagerS!(Tuple!(ReferenceCountedManager)) ret; 31 return ret; 32 } 33 34 /** 35 * Puts together a group of managers for use with managed!T 36 * 37 * See_Also: 38 * managed 39 */ 40 auto managers(U...)(U args) if (U.length > 0) { 41 return MemoryManagerS!(Tuple!U)(Tuple!U(args)); 42 } 43 44 /** 45 * Implements a managed memory model representation for heap allocated data. 46 * 47 * Ensures all memory allocated is accessed in a safe manner but may result in unsafe data usage. $(BR) 48 * Original concept was for language support $(WEB http://cattermole.co.nz/article/managed_memory, link). 49 * 50 * This can be thought of in terms of c++ const which is a head only const. A head const is where the pointer may not 51 * not change but the data is modifiable. For example an element in array may be changed but it may not be changed to 52 * another array. 53 * 54 * Compatibility with allocators are a requirement, using IAllocator to represent it. This is a optional element and 55 * will by default use theAllocator(). This allows fine grain control over how it allocates on the $(B heap). 56 * 57 * When using a COM object as the source, it will assume the responsiblity of AddRef and Releaseing instead 58 * of opInc + opDec. Ensuring deallocation when all references are removed. Rule: has secondary ownership and 59 * will not attempt to deallocate. But may deallocate at any time. 60 * 61 * A managed type is compatible with $(D @safe), "$(D @nogc)" and $(D nothrow). However only on the type being represented 62 * all of the methods upon $(D managed!T) itself is @trusted because of how allocators work. 63 * 64 * You may not cast away $(D managed!T) or get the real instance of the type. For classes you may cast to a more generic 65 * interface/class but not to a more specialised one. For basic types it may be done to another if its size is the same. 66 * If the usage will not escape it may be freely used as the real type without casting. 67 * 68 * When the managed type is an array, it will wrap all slice actions into returning a $(D managed!T) value. 69 * This means that all slices are always safe to use and will keep the entirety of the array around until dealloation. 70 * Otherwise in normal operations it will act as if it was a normal D array 71 * only using allocators for expanding and shrinking. 72 * 73 * To retrieve a null version, just use $(D (managed!T).init). It will directly compare to $(D null) as true. 74 * Since internally it will be comparing to a pointer while using $(D is). 75 * 76 * See_Also: 77 * IMemoryManager, ISharedMemoryManager, managers, ReferenceCountedManager, NeverDeallocateManager 78 */ 79 struct managed(Type) { 80 static assert(!is(Type==union), "managed!T does not support unions as head type"); 81 static assert(!isPointer!Type, "managed!T does not support pointers"); 82 83 static if (ManagedIsShared) { 84 alias AllocatorType = shared(ISharedAllocator); 85 alias __managedAllocator = processAllocator; 86 } else { 87 alias AllocatorType = IAllocator; 88 alias __managedAllocator = theAllocator; 89 } 90 91 /// Has this instance been initialized? 92 bool isNull() { 93 return __managedInternal.managers is null; 94 } 95 96 // \/ Construction \/ 97 98 static { 99 /// Constructs managed!T where T is a class 100 managed!Type opCall(MemoryManagers)(Type value, MemoryManagers managers, AllocatorType allocator=__managedAllocator) 101 if (is(Type==class) || is(Type==interface)) 102 in { 103 assert(value !is null, "Class instance must not be null."); 104 assert(allocator !is null, "Allocator instance must not be null"); 105 } body { 106 managed!Type ret; 107 108 ManagedInternalData.create(ret, managers, allocator); 109 ret.__managedInternal.value = value; 110 111 return ret; 112 } 113 114 /// Constructs managed!T where T is a struct or basic type 115 managed!Type opCall(MemoryManagers)(Type value, MemoryManagers managers, AllocatorType allocator=__managedAllocator) 116 if (isBasicType!Type || is(Type == struct)) 117 in { 118 assert(allocator !is null, "Allocator instance must not be null"); 119 } body { 120 managed!Type ret; 121 122 ManagedInternalData.create(ret, managers, allocator); 123 ret.__managedInternal.value = value; 124 125 return ret; 126 } 127 128 /// Constructs managed!T where T is an array with elements of class/struct/basic types 129 managed!Type opCall(MemoryManagers)(Type value, MemoryManagers managers, AllocatorType allocator=__managedAllocator) 130 if (isArray!Type) 131 in { 132 assert(value.length > 0, "Value instance must not be empty."); 133 assert(allocator !is null, "Allocator instance must not be null"); 134 } body { 135 managed!Type ret; 136 137 ManagedInternalData.create(ret, managers, allocator); 138 ret.__managedInternal.value = value; 139 140 return ret; 141 } 142 143 /// Constructs managed!T where T is a pointer to a class/struct/array/basic type 144 managed!Type opCall(MemoryManagers)(Type* value, MemoryManagers managers, AllocatorType allocator=__managedAllocator) 145 in { 146 assert(value !is null, "Value instance must not be null."); 147 assert(allocator !is null, "Allocator instance must not be null"); 148 } body { 149 managed!Type ret; 150 151 ManagedInternalData.create(ret, managers, allocator); 152 ret.__managedInternal.selfReUpdate = value; 153 154 return ret; 155 } 156 157 /// Constructs managed!T where T is a struct or class type with arguments 158 managed!Type opCall(MemoryManagers, ArgsT...)(MemoryManagers managers, Tuple!ArgsT args, AllocatorType allocator=__managedAllocator) 159 if (is(Type == class) || is(Type == struct)) 160 in { 161 assert(allocator !is null, "Allocator instance must not be null"); 162 } body { 163 managed!Type ret; 164 165 ManagedInternalData.create(ret, managers, allocator); 166 167 static if (is(Type == class)) { 168 ret.__managedInternal.value = allocator.make!Type(args.expand); 169 } else static if (is(Type == struct)) { 170 ret.__managedInternal.value = Type(args.expand); 171 } 172 173 return ret; 174 } 175 } 176 177 // /\ Construction /\ 178 // \/ Type conversions \/ 179 180 static if (!(is(Type == const) || is(Type == immutable))) { 181 /// Adds const 182 managed!(const(Type)) opCast(Type2)() if (is(Type2==managed!(const(Type)))) { 183 managed!(immutable(Type)) ret; 184 185 __managedInternal.copyInto(ret); 186 187 return ret; 188 } 189 190 /// Adds immutable 191 managed!(immutable(Type)) opCast(Type2)() if (is(Type2==managed!(immutable(Type)))) { 192 managed!(immutable(Type)) ret; 193 194 __managedInternal.copyInto(ret); 195 196 return ret; 197 } 198 } 199 200 static if (is(Type==class) || is(Type==interface)) { 201 /// Casts class to a more generic type 202 managed!(CopyConstness!(Type, Type2)) opCast(Type2:managed!Type2)() if (is(Type:Type2)) { 203 managed!(CopyConstness!(Type, Type2)) ret; 204 205 __managedInternal.copyInto(ret); 206 207 if (__managedInternal.selfReUpdate !is null) { 208 ret.__managedInternal.selfReUpdate = cast(Type2*)__managedInternal.selfReUpdate; 209 } else { 210 ret.__managedInternal.value = cast(Type2)__managedInternal.value; 211 } 212 213 return ret; 214 } 215 216 /// Unsafe-ish dynamic cast, check if is null! 217 managed!(CopyConstness!(Type, Type2)) opCast(Type2:managed!Type2)() if (!is(Type:Type2) && (is(Type2 == interface) || is(Type2 == class))) { 218 managed!(CopyConstness!(Type, Type2)) ret; 219 220 if (CopyConstness!(Type, Type2) v = cast(CopyConstness!(Type, Type2))__managedGet()) { 221 __managedInternal.copyInto(ret); 222 223 if (__managedInternal.selfReUpdate !is null) { 224 ret.__managedInternal.selfReUpdate = cast(Type2*)__managedInternal.selfReUpdate; 225 } else { 226 ret.__managedInternal.value = cast(Type2)__managedInternal.value; 227 } 228 } 229 230 return ret; 231 } 232 } else static if (is(Type:TypeIf_Class[], TypeIf_Class) && (is(TypeIf_Class == class) || is(TypeIf_Class==interface))) { 233 /// Casts an array of classes to a more generic class type 234 managed!(CopyConstness!(Type, Type2)[]) opCast(Type2:managed!(Type2[]))() if (is(Type:Type2)) { 235 managed!(CopyConstness!(Type, Type2)[]) ret; 236 237 __managedInternal.copyInto(ret); 238 239 return ret; 240 } 241 } else static if (isBasicType!Type || is(Type == struct)) { 242 /// Casts a basic type to a similar (aka same sized) type 243 managed!(CopyConstness!(Type, Type2)) opCast(Type2:managed!Type2)() 244 if ((isBasicType!Type2 || is(Type2 == struct)) && Type2.sizeof == Type.sizeof) { 245 managed!(CopyConstness!(Type, Type2)) ret; 246 247 __managedInternal.copyInto(ret); 248 249 return ret; 250 } 251 } else static if (is(Type:TypeIf_StructBasic[], TypeIf_StructBasic) && (isBasicType!TypeIf_StructBasic || is(TypeIf_StructBasic == struct))) { 252 /// Casts an array of structs or basic types to a similar (aka same sized) type 253 managed!(CopyConstness!(TypeIf_StructBasic, Type2)[]) opCast(Type2:managed!(Type2[]))() 254 if ((isBasicType!Type2 || is(Type2 == struct)) && Type2.sizeof == TypeIf_StructBasic.sizeof) { 255 managed!(CopyConstness!(TypeIf_StructBasic, Type2)[]) ret; 256 257 __managedInternal.copyInto(ret); 258 259 return ret; 260 } 261 } 262 263 // /\ Type convesions /\ 264 // \/ Reference counting stuff \/ 265 266 this(this) { 267 if (__managedInternal.managers is null) 268 return; 269 270 __managedInternal.managers.onIncrement(); 271 272 static if (__traits(compiles, {__managedInternal.value.onIncrement();})) { 273 __managedInternal.value.onIncrement(); 274 } 275 276 version(Windows) { 277 static if (is(MyType == class) && is(MyType : IUnknown)) { 278 __managedInternal.value.AddRef(); 279 } 280 } 281 } 282 283 ~this() { 284 if (__managedInternal.managers is null) 285 return; 286 287 __managedInternal.managers.onDecrement(); 288 289 static if (__traits(compiles, {__managedInternal.value.onDecrement();})) { 290 __managedInternal.value.onDecrement(); 291 } 292 293 bool beenReleased; 294 version(Windows) { 295 static if ((is(Type==class) || is(Type==interface)) && is(Type:IUnknown)) { 296 beenReleased = __managedInternal.value.Release() == 0; 297 } 298 } 299 300 if (beenReleased) { 301 __managedInternal.allocator.dispose(__managedInternal.managers); 302 } else if (__managedInternal.managers.shouldDeallocate) { 303 if (__managedInternal.selfReUpdate is null && !__managedInternal.managers.typeShouldNeverDeallocate) { 304 static if (is(Type==struct) || isBasicType!Type) { 305 } else static if (isDynamicArray!Type) { 306 __managedInternal.allocator.dispose(cast(Unqual!(ForeachType!Type)[])__managedInternal.value); 307 } else static if (is(Type == class) || is(Type == interface)) { 308 __managedInternal.allocator.dispose(cast(Unqual!Type)__managedInternal.value); 309 } 310 } 311 __managedInternal.allocator.dispose(__managedInternal.managers); 312 } else { 313 // do nothing 314 // the default behaviour from shouldDeallocate if it does not on call is true 315 } 316 } 317 318 // /\ Reference counting stuff /\ 319 // \/ Getters \/ 320 321 static if (isArray!Type) { 322 323 } 324 325 alias __managedGet this; 326 scope ref Type __managedGet() { 327 if (__managedInternal.selfReUpdate is null) 328 return __managedInternal.value; 329 else return *__managedInternal.selfReUpdate; 330 } 331 332 // /\ Getters /\ 333 // \/ Internal stuff \/ 334 335 private { 336 enum ManagedIsShared = is(Type == shared); 337 ManagedInternalData __managedInternal; 338 339 struct ManagedInternalData { 340 Type value; 341 Type* selfReUpdate; 342 343 static if (ManagedIsShared) { 344 shared(ISharedAllocator) allocator; 345 shared(ISharedMemoryManager) managers; 346 347 static void create(MemoryManagerST)(ref managed!Type ctx, MemoryManagerST managers, shared(ISharedAllocator) allocator) { 348 ctx.__managedInternal.allocator = allocator; 349 350 static if (is(MemoryManagerST : IMemoryManager)) { 351 ctx.__managedInternal.managers = managers.dup(alloc); 352 } else { 353 ctx.__managedInternal.managers = allocator.make!(shared(MemoryManager!(Type, MemoryManagerST))) 354 (allocator, managers); 355 } 356 357 ctx.__managedInternal.managers.onIncrement(); 358 } 359 } else { 360 IAllocator allocator; 361 IMemoryManager managers; 362 363 static void create(MemoryManagerST)(ref managed!Type ctx, MemoryManagerST managers, IAllocator allocator) { 364 ctx.__managedInternal.allocator = allocator; 365 366 static if (is(MemoryManagerST : IMemoryManager)) { 367 ctx.__managedInternal.managers = managers.dup(alloc); 368 } else { 369 ctx.__managedInternal.managers = allocator.make!(MemoryManager!(Type, MemoryManagerST)) 370 (allocator, managers); 371 } 372 373 ctx.__managedInternal.managers.onIncrement(); 374 } 375 } 376 377 void copyInto(Into)(ref managed!Into destination) { 378 destination.__managedInternal.value = cast(Into)value; 379 destination.__managedInternal.selfReUpdate = cast(Into*)selfReUpdate; 380 destination.__managedInternal.allocator = allocator; 381 destination.__managedInternal.managers = managers; 382 destination.__managedInternal.managers.onIncrement(); 383 } 384 } 385 } 386 387 // /\ Internal stuff /\ 388 } 389 390 /// 391 interface IMemoryManager { 392 IMemoryManager dup(IAllocator alloc); 393 394 /** 395 * on ~this() call 396 */ 397 bool shouldDeallocate() @safe nothrow; 398 399 /** 400 * Is the type even allowed to be deallocated? 401 */ 402 bool typeShouldNeverDeallocate() @safe nothrow; 403 404 /** 405 * this() 406 */ 407 void onIncrement() @safe nothrow; 408 /** 409 * ~this() 410 */ 411 void onDecrement() @safe nothrow; 412 } 413 414 /// 415 interface ISharedMemoryManager { 416 shared(ISharedMemoryManager) dup(shared(ISharedAllocator) alloc) shared; 417 418 419 /** 420 * Is the type even allowed to be deallocated? 421 */ 422 bool typeShouldNeverDeallocate() @safe nothrow shared; 423 424 /** 425 * on ~this() call 426 */ 427 bool shouldDeallocate() @safe nothrow shared; 428 429 /** 430 * this() 431 */ 432 void onIncrement() @safe nothrow shared; 433 /** 434 * ~this() 435 */ 436 void onDecrement() @safe nothrow shared; 437 } 438 439 /// 440 struct ReferenceCountedManager { 441 private import core.atomic : atomicOp; 442 shared(int) counter; 443 444 @safe nothrow { 445 bool shouldDeallocate() { return counter <= 0; } 446 bool shouldDeallocate() shared { return counter <= 0; } 447 448 void onIncrement() { atomicOp!"+="(counter, 1); } 449 void onIncrement() shared { atomicOp!"+="(counter, 1); } 450 451 void onDecrement() { atomicOp!"-="(counter, 1); } 452 void onDecrement() shared { atomicOp!"-="(counter, 1); } 453 } 454 } 455 456 /// 457 struct NeverDeallocateManager { 458 bool typeShouldNeverDeallocate() @safe nothrow { 459 return true; 460 } 461 462 bool typeShouldNeverDeallocate() @safe nothrow shared { 463 return true; 464 } 465 } 466 467 private { 468 struct MemoryManagerS(Type) { 469 Type managers; 470 alias managers this; 471 472 this(Type v) { 473 managers = v; 474 } 475 } 476 477 final class MemoryManager(UserType, UserManagers) : IMemoryManager if (!is(UserType == shared) && 478 isValidUserManagers!(UserType, UserManagers)) { 479 480 UserManagers managers, originalManagers; 481 bool[UserManagers.length] doDeAllocateMemMgrs; 482 IAllocator allocator; 483 484 this(IAllocator allocator, UserManagers managers) { 485 this.allocator = allocator; 486 this.managers = managers; 487 this.originalManagers = managers; 488 489 foreach(i, ref manager; managers) { 490 static if (is(typeof(manager) == class)) { 491 if (manager is null) { 492 manager = allocator.make!(typeof(manager)); 493 doDeAllocateMemMgrs[i] = true; 494 } 495 } 496 } 497 } 498 499 bool shouldDeallocate() @safe nothrow { 500 bool hadOne; 501 502 static if (UserManagers.length >= 1) { 503 foreach(ref manager; managers) { 504 static if (__traits(compiles, {bool ret = manager.shouldDeallocate();})) { 505 bool ret = manager.shouldDeallocate(); 506 hadOne = true; 507 508 if (ret) 509 return ret; 510 } 511 } 512 } 513 514 return !hadOne; 515 } 516 517 bool typeShouldNeverDeallocate() @safe nothrow { 518 bool hadOne; 519 520 static if (UserManagers.length >= 1) { 521 foreach(ref manager; managers) { 522 static if (__traits(compiles, {bool ret = manager.typeShouldNeverDeallocate();})) { 523 bool ret = manager.typeShouldNeverDeallocate(); 524 hadOne = true; 525 526 if (!ret) 527 return ret; 528 } 529 } 530 } 531 532 return hadOne; 533 } 534 535 void onIncrement() @safe nothrow { 536 foreach(ref manager; managers) { 537 static if (__traits(compiles, {manager.onIncrement();})) { 538 manager.onIncrement(); 539 } 540 } 541 } 542 543 void onDecrement() @safe nothrow { 544 foreach(ref manager; managers) { 545 static if (__traits(compiles, {manager.onDecrement();})) { 546 manager.onDecrement(); 547 } 548 } 549 } 550 551 IMemoryManager dup(IAllocator alloc) { 552 return alloc.make!(MemoryManager!(UserType, UserManagers))(alloc, originalManagers); 553 } 554 555 ~this() { 556 foreach(i, ref manager; managers) { 557 static if (is(typeof(manager) == class)) { 558 if (doDeAllocateMemMgrs[i]) { 559 allocator.dispose(manager); 560 } 561 } 562 } 563 } 564 } 565 566 final class MemoryManager(UserType, UserManagers) : ISharedMemoryManager if (is(UserType == shared) && 567 isValidUserManagers!(UserType, UserManagers)) { 568 569 UserManagers managers, originalManagers; 570 bool[UserManagers.length] doDeAllocateMemMgrs; 571 shared(ISharedAllocator) allocator; 572 573 this(shared(ISharedAllocator) allocator, UserManagers managers) shared { 574 this.allocator = allocator; 575 this.managers = managers; 576 this.originalManagers = managers; 577 578 foreach(i, ref manager; managers) { 579 static if (is(typeof(manager) == class)) { 580 if (manager is null) { 581 manager = allocator.make!(typeof(manager)); 582 doDeAllocateMemMgrs[i] = true; 583 } 584 } 585 } 586 } 587 588 bool shouldDeallocate() @safe nothrow shared { 589 bool hadOne; 590 591 static if (UserManagers.length >= 1) { 592 foreach(ref manager; managers) { 593 static if (__traits(compiles, {bool ret = manager.shouldDeallocate();})) { 594 bool ret = manager.shouldDeallocate(); 595 hadOne = true; 596 597 if (!ret) 598 return ret; 599 } 600 } 601 } 602 603 return !hadOne; 604 } 605 606 607 bool typeShouldNeverDeallocate() @safe nothrow shared { 608 bool hadOne; 609 610 static if (UserManagers.length >= 1) { 611 foreach(ref manager; managers) { 612 static if (__traits(compiles, {bool ret = manager.typeShouldNeverDeallocate();})) { 613 bool ret = manager.typeShouldNeverDeallocate(); 614 hadOne = true; 615 616 if (ret) 617 return ret; 618 } 619 } 620 } 621 622 return hadOne; 623 } 624 625 void onIncrement() @safe nothrow shared { 626 foreach(ref manager; managers) { 627 static if (__traits(compiles, {manager.onIncrement();})) { 628 manager.onIncrement(); 629 } 630 } 631 } 632 633 void onDecrement() @safe nothrow shared { 634 foreach(ref manager; managers) { 635 static if (__traits(compiles, {manager.onDecrement();})) { 636 manager.onDecrement(); 637 } 638 } 639 } 640 641 shared(ISharedMemoryManager) dup(shared(ISharedAllocator) alloc) shared { 642 return alloc.make!(shared(MemoryManager!(UserType, UserManagers)))(alloc, originalManagers); 643 } 644 645 ~this() shared { 646 foreach(i, ref manager; managers) { 647 static if (is(typeof(manager) == class)) { 648 if (doDeAllocateMemMgrs[i]) { 649 allocator.dispose(manager); 650 } 651 } 652 } 653 } 654 } 655 656 bool isValidUserManagers(UserType, Ts)() pure { 657 bool ret = true; 658 659 foreach(T; Ts.Types) { 660 static if ((is(UserType == shared) == is(T == shared)) && !(is(T == class) || is(T == struct))) { 661 ret = false; 662 } 663 } 664 665 return ret; 666 } 667 }